package com.tankgame.its381b;

import java.io.IOException;
import java.util.Random;
import java.util.Stack;
import java.util.Vector;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.lcdui.game.LayerManager;

public class tankgame
        extends GameCanvas implements Runnable {

    private static final int SLEEP_INITIAL = 50;
    private Random random;
    private int sleepTime = SLEEP_INITIAL;
    private volatile Thread thread;
    public LayerManager mLayerManager;
    private BackgroundTiledLayer background;
    private TankSprite tankSprite;
    private BossSprite boss;
    private Vector bulletCollection;
    private Vector invaderCollection;
    private Vector cloudCollection;
    public final int CENTER_X = getWidth() / 2;
    public final int CENTER_Y = getHeight() / 2;
    public final int GAME_ORIGIN_X = (getWidth() - Bounds.WIDTH) / 2;
    public final int GAME_ORIGIN_Y = (getHeight() - Bounds.HEIGHT) / 2;
    public final int SECTION_HEIGHT = 48;
    public final int BASEX = 155;
    public final int BASEY = GAME_ORIGIN_Y + Bounds.HEIGHT - SECTION_HEIGHT;
    public final int MAX_HEIGHT = 32;
    private double turretAngle = -180.0;
    private int score;
    private boolean gameOn, win;
    private int maxInvaders = 5;
    private int levelchange = 2;
    private int levelup = 200;
    private int level = 1;
    public int bossHealth = 20;

    
    //tankgame class, inititates layer manager and creates tank and background, sets up vectors for bullets invaders and clouds
    public tankgame(boolean suppress) {
        super(suppress);
        try {
            gameOn = true;
            win = false;
            mLayerManager = new LayerManager();
            mLayerManager.append(tankSprite = new TankSprite());
            tankSprite.setPosition(BASEX, BASEY);
            mLayerManager.append(background = new BackgroundTiledLayer());
            // set the location of the background
            background.setPosition(GAME_ORIGIN_X, GAME_ORIGIN_Y);
            random = new Random();
            bulletCollection = new Vector();
            invaderCollection = new Vector();
            for (int i = 0; i < maxInvaders; i++) {
                InvaderSprite s = new InvaderSprite();
                invaderCollection.addElement(s);
                mLayerManager.insert(s, 0);
            }
            cloudCollection = new Vector();
        } catch (Exception e) {
            System.out.println("Error creating the Tank GameCanvas.");
        }
    }

    // When the canvas is shown, start a thread to
    // run the game loop.
    protected void showNotify() {
        thread = new Thread(this);
        thread.start();
        System.out.println("showNotify");
    }

    protected void hideNotify() {
        System.out.println("hideNotify");
    }

    public void start() {
    }

    // run, dictates what will happen while gameOn is true
    public void run() {
        Graphics g = getGraphics();
        while (gameOn) { // infinite loop
            // based on the structure

            // check user's input
            checkUserInput();

            // update screen
            updateGameScreen(g);

            // verify game state
            verifyGameState();

            // and sleep, this controls
            // how fast refresh is done
            try {
                Thread.sleep(sleepTime);
            } catch (Exception e) {
            }
        }
        // End of game screen
        showFarewell(g);
    }

    //VerifyGameState, sets events that will happen when game state is verified
    private void verifyGameState() {
    	//creation of stacks for each item that has mulitple instances
        Stack bulletStack = new Stack();
        Stack invaderStack = new Stack();
        Stack cloudStack = new Stack();
        
        //check against bullet and invader to detect a collision, remove both items if true and increase score by 25
        for (int i = 0; i < this.bulletCollection.size(); i++) {
            for (int j = 0; j < invaderCollection.size(); j++) {
                if (((bullet) bulletCollection.elementAt(i)).collidesWith(
                        ((InvaderSprite) invaderCollection.elementAt(j)), true) == true) {
                    bulletStack.push(bulletCollection.elementAt(i));
                    invaderStack.push(invaderCollection.elementAt(j));
                    score += 25;
                }
        }
        
        // check agains bullet and boss if he is their if a collision is detected the bullet is removed and boss health is lowered by 2
        // when boss health reaches 0 score is increased by 500, boss is removed, game ends and win value is set to true
        for (int h = 0; h < this.bulletCollection.size(); h++) {
            if (boss != null){
                if (((bullet)bulletCollection.elementAt(h)).collidesWith((boss), true) == true) {
                        bulletStack.push(bulletCollection.elementAt(h));
                        if (bossHealth > 0) {
                            bossHealth -= 2;
                        }else if (bossHealth <= 0){
                            mLayerManager.remove(boss);
                            score += 500;
                            gameOn = false;
                            win = true;
                    }

                }
            }

        for (int l = 0; l < bulletCollection.size(); l++){
            if (((bullet)bulletCollection.elementAt(l)).getY() <= 30){
                bulletStack.push(bulletCollection.elementAt(l));
            }
        }
        // check against cloud and X location on grid to remove clouds as they reach far end of game frame
        for (int k = 0; k < cloudCollection.size(); k++){
            if (((CloudSprite) cloudCollection.elementAt(k)).getX() >= 160){
                cloudStack.push(cloudCollection.elementAt(k));
            }
        }
        
        
        // while loops to remove bullets, invaders and clouds from the stack and the Layer Manager
        while (!bulletStack.empty()) {
            bullet b = (bullet) bulletStack.pop();
            mLayerManager.remove(b);
            bulletCollection.removeElement(b);
        }
        while (!invaderStack.empty()) {
            InvaderSprite z = (InvaderSprite) invaderStack.pop();
            mLayerManager.remove(z);
            invaderCollection.removeElement(z);
        }
        while (!cloudStack.empty()){
            CloudSprite c = (CloudSprite)cloudStack.pop();
            mLayerManager.remove(c);
            cloudCollection.removeElement(c);
        }
        
        //if statements to determine creation of boss or invader and cloud
        if (level == 5){
            if (boss == null){
                createBoss();
            }
        }else if (level < 5) {
        createInvader();
        }
        if (random.nextInt(50)== 5){
        createCloud();
        }
        
        //if statements to change max invaders at certain levels and level at certain scores
        if (level == levelchange){
            levelchange +=2;
            maxInvaders +=1;
        }
        if (score >= levelup) {
            level += 1;
            levelup += levelup;
            
        } 
    }
}
}

    // show Score and Level for player tracking during game
    private void showScore(Graphics g) {
        Font font = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_SMALL);
        g.setFont(font);
        g.setColor(0x000000);
        g.drawString("Score=" + score, 80, 10, 0);
    }
    private void showLevel(Graphics g) {
        Font font = Font.getFont(Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_SMALL);
        g.setFont(font);
        g.setColor(0x000000);
        g.drawString("Level=" + level, 10, 10, 0);
    }
    
    // Farewell message depending on gameon boolean value and win boolean value
    private void showFarewell(Graphics g) {
        String farewell = "Thanks for Playing!";
        if (win) {
            farewell = "Win Score: " + score;
        }
        g.setColor(0x0000ff);
        g.fillRect(25, 45, 125, 30);
        g.setColor(0xffffff);
        Font font = Font.getFont(Font.FACE_PROPORTIONAL, Font.STYLE_BOLD, Font.SIZE_MEDIUM);
        g.setFont(font);
        g.drawString(farewell, 42, 50, 0);
        repaint();
    }
    
    // checks the number of clouds and if there are less than 15 creates a new cloud and adds it to the cloudcollection and layermanager
    public void createCloud() {
        if (cloudCollection.size() < 15) {
            try {
                CloudSprite c = new CloudSprite();
                cloudCollection.addElement(c);
                mLayerManager.insert(c,2);
            }
            catch (Exception e){
                System.out.println("Cannot create Cloud");
            }
        }
    }
    // creates a boss and inserts it into the layer manager
    public void createBoss() {
        try {
               boss = new BossSprite();
               mLayerManager.insert(boss, 0);
        }
        catch(Exception e){
            System.out.println("Cannot create Boss");
        }
    }
    // checks the number of invaders against the variable maxInvaders and creates a new invader and adds it to the invader collection and layermanager
    public void createInvader() {
        if (invaderCollection.size() < maxInvaders){
            try {
                InvaderSprite s = new InvaderSprite();
                invaderCollection.addElement(s);
                mLayerManager.insert(s, 0);
            }
            catch (Exception e){
                System.out.println("Cannot create invader.");
              }
        }
    }
    // creates a bullet at tank X and Y with a movement path of of the turretAngle
    public void createBullet() {
        if (bulletCollection == null) {
            bulletCollection = new Vector();
        }
        try {
            bullet b = new bullet(tankSprite.getX(), tankSprite.getY(), turretAngle);
            bulletCollection.addElement(b);
            mLayerManager.insert(b, 0);
        } catch (Exception e) {
            System.out.println("Cannot create bullet.");
        }
    }

    // This method catches the continually pressed events
    private void checkUserInput() {
        // get the state of keys
        int keyState = getKeyStates();
        aim(keyState);
    }

    // This method catches the single press events
    protected void keyPressed(int key) {
        int action = getGameAction(key);
        if (action == FIRE) {
            createBullet();
        }
    }

    public void updateGameScreen(Graphics g) {
        // the next two lines clear the background to white
        g.setColor(0xffffff); // RGB FF-FF-FF
        g.fillRect(0, 0, getWidth(), getHeight());
        // draw the items in the LayerManager
        
        //for each item in the bulletCollection invoke move
        for (int i = 0; i < this.bulletCollection.size(); i++) {
            ((bullet) bulletCollection.elementAt(i)).move();
            //System.out.println("moving bullet " + i);
            }
        
        //for each item in invaderCollection invoke move and check Y coordinate, if Y is ground game ends win is false
        for (int i = 0; i < invaderCollection.size(); i++) {
            ((InvaderSprite) invaderCollection.elementAt(i)).move();
            if (((InvaderSprite) invaderCollection.elementAt(i)).getY() >= (BASEX - 10
                    )) {
                gameOn = false;
                win = false;
            }
        }
        
        //for each item in cloudCollection invoke move
        for (int i = 0; i < cloudCollection.size(); i++){
            ((CloudSprite) cloudCollection.elementAt(i)).move();
        }
        
        //for boss if it is present move
        if(boss != null){
            boss.move();
        }
        
        //move tank
        tankSprite.move();
        
        //paint LayerManger, score, level and turret
        mLayerManager.paint(g, 0, 0);
        showScore(g);
        showLevel(g);
        drawTurret(g);
        // this call paints off screen buffer to screen
        flushGraphics();
    }

    //draw the turret(a line) from cx,cy to x,y
    private void drawTurret(Graphics g) {
        /***
         *  The parametric equation for a circle is
         *  x = cx + r * cos(a)
         *  y = cy + r * sin(a)
         *
         ***/
        double x, y,
        		//cx and cy are center of the tank
                cx = (double) tankSprite.getX() + 8,
                cy = (double) tankSprite.getY() + 6.5,
                r = 8.0;
        //x and y are determined by the angle of the turret with a radius of r from cx,cy
        x = cx + r * Math.cos(Math.toRadians(turretAngle));
        y = cy + r * Math.sin(Math.toRadians(turretAngle));
        g.setColor(0x000000); // black pen to draw turret
        g.drawLine((int) cx, (int) cy, (int) x, (int) y);
    }

    // aim to calculate turret angle, each push of left or right alters value by 2
    private void aim(int keyState) {

        // determines which way to move and changes the
        // x coordinate accordingly
        if ((keyState & LEFT_PRESSED) != 0) {
            turretAngle -= 2.0;
            if (turretAngle < -180.0) {
                turretAngle = -180.0;
            }
        } else if ((keyState & RIGHT_PRESSED) != 0) {
            turretAngle += 2.0;
            if (turretAngle > 0.0) {
                turretAngle = 0.0;
            }
        }
    }
}
   